using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace LS.Editor
{
    public interface IDirtyHandler<T> : DirtyWork.IDirtyHandler where T : class, new()
    {
        public void Excute(T obj);
    }

    public class DirtyWork
    {
        private Dictionary<Type, IDirtyHandler> m_Handlers;
        private HashSet<object> m_DirtyObjects;

        public DirtyWork()
        {
            m_Handlers = new Dictionary<Type, IDirtyHandler>();
            m_DirtyObjects = new HashSet<object>();
        }

        public void RegistDirtyHandler(IDirtyHandler handler)
        {
            m_Handlers[handler.TargetType] = handler;
            handler.SetupOwner(this);
        }

        public void RegistDirtyHandler<THandler, TObject>()
            where THandler : class, IDirtyHandler<TObject>, new()
            where TObject: class, new()
        {
            Type objType = typeof(TObject);
            if (!m_Handlers.ContainsKey(objType))
            {
                var handler = new THandler();
                m_Handlers.Add(objType, handler);
                handler.SetupOwner(this);
            }
        }

        public void RemoveDirtyHandler<T>() where T : class, new()
        {
            Type objType = typeof(T);
            if (m_Handlers.TryGetValue(objType,out var handler))
            {
                handler.SetupOwner(null);
            }
            m_Handlers.Remove(objType);
        }

        public void SetDirty<T>(T obj) where T: class, new()
        {
            Type t = typeof(T);
            if (m_Handlers.ContainsKey(t))
            {
                m_DirtyObjects.Add(obj);
            }
            else
            {
                Debug.LogWarning($"Type {t.FullName} doesn't have DirtyHandler. It will not be handle");
            }
        }

        public void RemoveDirty<T>(T obj) where T : class, new()
        {
            Type t = typeof(T);
            if (m_DirtyObjects.Contains(t))
            {
                m_DirtyObjects.Remove(obj);
            }
        }

        public void ClearDirty()
        {
            m_DirtyObjects.Clear();
        }

        public void DoDirty()
        {
            int index = 0;
            //当脏标记物体数量不为0时进入循环清理过程，设置一个循环上限防止进入无限循环
            while (m_DirtyObjects.Count > 0 && index < 5)
            {
                object[] objs = m_DirtyObjects.ToArray();
                m_DirtyObjects.Clear();
                foreach (object item in objs)
                {
                    Type type = item.GetType();
                    if (m_Handlers.TryGetValue(type, out var handler))
                    {
                        handler.Excute(item);
                    }
                }
                index++;
            }
        }

        public interface IDirtyHandler
        {
            public DirtyWork OwnerWork { get; }
            public Type TargetType { get; }
            public void Excute(object obj);
            public void SetupOwner(DirtyWork owner);
        }
    }
}
